﻿// Copyright (c) Inossem. All rights reserved.
// Licensed under the LGPL-3.0 license. See LICENSE file in the project root for full license information.
using DWF.Studio.RemoteCtrls.Proxy;
using DWF.Studio.Share.Log;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Policy;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms.VisualStyles;

namespace DWF.Studio.RemoteCtrls
{
    /// <summary>
    /// 控件远程加载器
    /// </summary>
    [Serializable]
    public class CtrlRemoteLoader
    {
        /// <summary>
        /// 应用程序域
        /// </summary>
        private AppDomain _appDomain;

        /// <summary>
        /// 程序域解析路径
        /// </summary>
        private List<string> _assemblyResolutionPaths = new List<string>();

        /// <summary>
        /// 
        /// </summary>
        private List<string> _assemblyShadowPaths = new List<string>();

        private Func<Dictionary<string, string>> _projectPropertiesFunc = null;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="resolutionPaths"></param>
        public CtrlRemoteLoader(string[] resolutionPaths)
        {
            if (resolutionPaths != null && resolutionPaths.Length > 0)
            {
                _assemblyResolutionPaths.AddRange(resolutionPaths);
            }
        }

        public CtrlRemoteLoader(string[] resolutionPaths, string[] shadowPaths)
            : this(resolutionPaths)
        {
            if (shadowPaths != null && shadowPaths.Length > 0)
            {
                _assemblyShadowPaths.AddRange(shadowPaths);
            }
        }

        public CtrlRemoteLoader(Func<Dictionary<string, string>> projectPropertiesFunc, string[] resolutionPaths, string[] shadowPaths)
            : this(resolutionPaths, shadowPaths)
        {
            _projectPropertiesFunc = projectPropertiesFunc;
        }

        /// <summary>
        /// 加载设计器代理
        /// </summary>
        /// <param name="projectDirectory"></param>
        /// <returns></returns>
        public ActivityDesignerProxy LoadDesignerProxy()
        {
            if (_appDomain == null)
            {
                lock (this)
                {
                    if (_appDomain == null)
                    {
                        createAppDomain();
                    }
                }
            }

            string name = Assembly.GetExecutingAssembly().GetName().FullName;
            var designerProxy = (ActivityDesignerProxy)_appDomain.CreateInstanceAndUnwrap(name, typeof(ActivityDesignerProxy).FullName, false, BindingFlags.Default, null, new object[] { _projectPropertiesFunc }, null, null);//封送

            return designerProxy;
        }

        /// <summary>
        /// 加载工具栏代理
        /// </summary>
        /// <returns></returns>
        public ActivityToolBoxProxy LoadToolBoxProxy()
        {
            if (_appDomain == null)
            {
                lock (this)
                {
                    if (_appDomain == null)
                    {
                        createAppDomain();
                    }
                }
            }

            string name = Assembly.GetExecutingAssembly().GetName().FullName;
            var toolBoxProxy = (ActivityToolBoxProxy)_appDomain.CreateInstanceAndUnwrap(name, typeof(ActivityToolBoxProxy).FullName);//封送

            return toolBoxProxy;
        }

        /// <summary>
        /// 加载解析器代理
        /// </summary>
        /// <returns></returns>
        public XamlAnalyzerProxy LoadXamlAnalyzerProxy()
        {
            if (_appDomain == null)
            {
                lock (this)
                {
                    if (_appDomain == null)
                    {
                        createAppDomain();
                    }
                }
            }

            string name = Assembly.GetExecutingAssembly().GetName().FullName;
            var analyzerProxy = (XamlAnalyzerProxy)_appDomain.CreateInstanceAndUnwrap(name, typeof(XamlAnalyzerProxy).FullName);

            return analyzerProxy;
        }

        /// <summary>
        /// 加载托转代理
        /// </summary>
        /// <returns></returns>
        public DragDropProxy LoadDragDropProxy()
        {
            if (_appDomain == null)
            {
                lock (this)
                {
                    if (_appDomain == null)
                    {
                        createAppDomain();
                    }
                }
            }

            string name = Assembly.GetExecutingAssembly().GetName().FullName;
            var dragDropProxy = (DragDropProxy)_appDomain.CreateInstanceAndUnwrap(name, typeof(DragDropProxy).FullName);

            return dragDropProxy;
        }

        /// <summary>
        /// 加载片段管理器代理
        /// </summary>
        /// <returns></returns>
        public SnippetExplorerProxy LoadSnippetExplorerProxy()
        {
            if (_appDomain == null)
            {
                lock (this)
                {
                    if (_appDomain == null)
                    {
                        createAppDomain();
                    }
                }
            }
            string name = Assembly.GetExecutingAssembly().GetName().FullName;
            var snippetExplorerProxy = (SnippetExplorerProxy)_appDomain.CreateInstanceAndUnwrap(name, typeof(SnippetExplorerProxy).FullName);//封送

            return snippetExplorerProxy;
        }
        /// <summary>
        /// 加载查询器代理
        /// </summary>
        /// <returns></returns>
        public SearchExplorerProxy LoadSearchProxy()
        {
            if (_appDomain == null)
            {
                lock (this)
                {
                    if (_appDomain == null)
                    {
                        createAppDomain();
                    }
                }
            }
            string name = Assembly.GetExecutingAssembly().GetName().FullName;
            var searchProxy = (SearchExplorerProxy)_appDomain.CreateInstanceAndUnwrap(name, typeof(SearchExplorerProxy).FullName);//封送

            return searchProxy;
        }
        private DomainHandleProxy loadDomainHandleProxy()
        {
            if (_appDomain == null)
            {
                lock (this)
                {
                    if (_appDomain == null)
                    {
                        createAppDomain();
                    }
                }
            }

            string name = Assembly.GetExecutingAssembly().GetName().FullName;
            var handle = (DomainHandleProxy)_appDomain.CreateInstanceAndUnwrap(name, typeof(DomainHandleProxy).FullName);

            return handle;
        }

        /// <summary>
        /// 创建程序域
        /// </summary>
        private void createAppDomain()
        {
            StringBuilder shadowPathBuilder = new StringBuilder();
            foreach (var shadowPath in _assemblyShadowPaths)
            {
                shadowPathBuilder.Append(shadowPath);
                shadowPathBuilder.Append(";");
            }

            AppDomainSetup info = new AppDomainSetup
            {
                ApplicationName = "RemoteCtrlDomain",
                LoaderOptimization = LoaderOptimization.MultiDomain,
                ApplicationBase = AppDomain.CurrentDomain.BaseDirectory,
                ShadowCopyFiles = "true",
                ShadowCopyDirectories = shadowPathBuilder.ToString(),
                CachePath = AppDomain.CurrentDomain.BaseDirectory,
                ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile
            };
            _appDomain = AppDomain.CreateDomain("RemoteControl", AppDomain.CurrentDomain.Evidence, info, AppDomain.CurrentDomain.PermissionSet, Array.Empty<StrongName>());
            _appDomain.AssemblyResolve += appDomain_AssemblyResolve;
            _appDomain.DomainUnload += (sender, e) => //andy 2020.07.28 添加 改用appDomain的卸载事件 处理Dispatcher的销毁
            {
                System.Windows.Threading.Dispatcher.CurrentDispatcher.InvokeShutdown();
            };
        }

        /// <summary>
        /// 程序域加载程序集解析
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        private Assembly appDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            Assembly[] loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
            foreach (Assembly asm in loadedAssemblies)
            {
                if (asm.FullName.Equals(args.Name))
                    return asm;
            }

            AssemblyName asmInfo = new AssemblyName(args.Name);
            for (int i = 0; i < this._assemblyResolutionPaths.Count; i++)
            {
                string asmFilePath = Path.Combine(this._assemblyResolutionPaths[i], asmInfo.Name + ".dll");
                if (File.Exists(asmFilePath))
                {
                    return Assembly.LoadFrom(asmFilePath);
                }
                else
                {
                    asmFilePath = Path.Combine(this._assemblyResolutionPaths[i], asmInfo.Name + ".exe");
                    if (File.Exists(asmFilePath))
                        return Assembly.LoadFrom(asmFilePath);
                }
            }

            return null;
        }
        /// <summary>
        /// 卸载
        /// </summary>
        public void Unload()
        {
            if (_appDomain != null)
            {
                try
                {
                    AppDomain.Unload(_appDomain);
                }
                catch (Exception ex)
                {
                    LogHelper.Error(ex);
                }
                finally
                {
                    _appDomain = null;
                    GC.Collect();
                }
            }
        }
    }
}
